#SIMPLE SAFE VIRTUAL MACHINE

class SpaghettiVM:
  
    registers ={ "A":None, "B":None, "C":None, "D":None, "E":None, "F":None, "G":None, "I":0}
    registered_function = {}
    labels = {}
    stack = []
    call_stack = []
 
    def attach_interrupt(self,interrupt,function,input_registers=[],output_registers=[]):
        self.registered_function[interrupt] = (function,input_registers,output_registers)
     
    def get_reg(self,reg):
        if reg in self.registers:
            pass
        else:
            self.registers[reg] = 0
        return self.registers[reg]
        
    def set_reg(self,reg,val):
        self.registers[reg] = val
 
    def log(self,msg):
        pass
    
    def assert_failed(self,msg,i):
        pass
    
    def execute(self,code):
        lines_of_code = code.split('\n')
        
        j=0
        while j < len(lines_of_code):
            line_of_code = lines_of_code[j]
            if  line_of_code.find(":") != -1:
                label = line_of_code.split(":")[0].strip()
                self.labels[label] = j
            j = j + 1
            
        compare_register_A = "A"
        compare_register_B = "A"
        j=0
        while j < len(lines_of_code):
            self.registers["I"] = j
            line_of_code = lines_of_code[j]
            #make sure this is a real line of code
            if line_of_code.strip() != "":
                #remove comments and random white space on left and right from line
                line_of_code = line_of_code.split('#')[0].strip()
     
                #skip over labels
                if  line_of_code.find(":") != -1:
                    j = j + 1
                    continue
     
                if line_of_code.find(' ') != -1:
                    opcode = line_of_code[0:line_of_code.find(' ')].strip()
                else:
                    opcode = line_of_code.strip()

                if opcode == "RET":
                    if len(self.call_stack) == 0:
                        return
                    else:
                        j = self.call_stack.pop()
                        continue
                elif opcode == "FSM":
                    self.log("Flying Spaghetti Monster lives!")
                
                params = line_of_code[line_of_code.find(' ')+1:].split(',')
                
                if opcode == "MOV":
                    p0 = params[0]
                    p1 = ",".join(params[1:])
                    if p1[0] == '"':
                        self.registers[p0] = p1[1:-1]
                    elif p1.isdigit():
                        self.registers[p0] = int(p1)
                    else:
                        self.registers[p0] = self.registers[p1]
                elif opcode == "SETE":
                    p0 = None
                    if params[0].isdigit():
                        p0 = int(params[0])
                    elif params[0][0] == '"':
                        p0 = params[0][1:-1]
                    
                    p1 = ",".join(params[1:])
                    if p1[0] == '"':
                        self.registers["D"][p0] = p1[1:-1]
                    elif p1.isdigit():
                        self.registers["D"][p0] = int(p1)
                    else:
                        self.registers["D"][p0] = self.registers[p1]
                elif opcode == "GETE":
                    p0 = params[0]
                    p1 = ",".join(params[1:])
                    val = None
                    if p1[0] == '"':
                        val = p1[1:-1]
                    elif p1.isdigit():
                        val = int(p1)
                    else:
                        val = self.registers[p1]
                    self.registers[p0] = self.registers["D"][val]
                elif opcode == "ADDE":
                    val = None
                    p = ",".join(params[:])
                    if p[0] == '"':
                        val = p[1:-1]
                    elif p.isdigit():
                        val = int(p)
                    else:
                        val = self.registers[p]
                        
                    if self.registers["D"] == None:
                        self.registers["D"] = [val]
                    else:
                        l = self.registers["D"]
                        l.append(val)
                        self.registers["D"] = l
                elif opcode == "LOG":
                    p = ",".join(params[0:])
                    if p[0] == '"':
                        self.log(p[1:-1])
                elif opcode == "INC":
                    self.registers[params[0]] = int(self.registers[params[0]]) + 1
                elif opcode == "DEC":
                    self.registers[params[0]] = int(self.registers[params[0]]) - 1
                elif opcode == "ADD":
                    self.registers[params[0]] = int(self.registers[params[0]]) + int(self.registers[params[1]])
                elif opcode == "SUB":
                    self.registers[params[0]] = int(self.registers[params[0]]) - int(self.registers[params[1]])
                elif opcode == "MUL":
                    self.registers[params[0]] = int(self.registers[params[0]]) * int(self.registers[params[1]])
                elif opcode == "DIV":
                    self.registers[params[0]] = int(self.registers[params[0]]) / int(self.registers[params[1]])
                elif opcode == "INT":
                    function,input_regs,output_regs = self.registered_function[int(params[0])]
                    input = []
                    for i in input_regs:
                        input.append(self.registers[i])
                    result = function(*input)
                    for i in range(0,len(output_regs)):
                        self.registers[output_regs[i]] = int(result[i])
                elif opcode == "CMP":
                    compare_register_A = params[0]
                    compare_register_B = params[1]
                elif opcode == "CALL":
                    self.call_stack.append(j+1)
                    j = self.labels[params[0]]
                    continue
                elif opcode == "LOOP":
                    if self.registers["C"] != 0:
                        self.registers["C"] = self.registers["C"] - 1
                        j = self.labels[params[0]]
                        continue
                elif opcode == "PUSH":
                    self.stack.append(self.get_reg(params[0]))
                elif opcode == "POP":
                    self.set_reg(params[0],self.stack.pop())
                elif opcode == "JMP":
                    j = self.labels[params[0]]
                    continue
                elif opcode == "JE":
                    if self.get_reg(compare_register_A) == self.get_reg(compare_register_B):
                        j = self.labels[params[0]]
                        continue
                elif opcode == "JNE":
                    if self.get_reg(compare_register_A) != self.get_reg(compare_register_B):
                        j = self.labels[params[0]]
                        continue
                elif opcode == "AE":
                    if self.get_reg(compare_register_A) != self.get_reg(compare_register_B):
                        self.assert_failed(params[0][1:-1],j)
                        return
                elif opcode == "ANE":
                    if self.get_reg(compare_register_A) == self.get_reg(compare_register_B):
                        self.assert_failed(params[0][1:-1],j)
                        return
            j = j + 1